#include <gl/glew.h>


#include <QOpenGLContext>
#include <QOpenGLFunctions>

#include <osgViewer/GraphicsWindow>
#include <osgGA/TrackballManipulator>
#include "OsgGraphicView.h"
//#include "EarthWrapper.h"
//#include <QDebug>
//#include <osgViewer/ViewerEventHandlers>

#include <amk3/SysConfig.h>


class OsgGraphicsContextAdapter : public osgViewer::GraphicsWindow
{
public:
    OsgGraphicsContextAdapter(OsgGraphicView* v){
        this->_view = v;

        setState( new osg::State );
        getState()->setGraphicsContext(this);
        getState()->setContextID( osg::GraphicsContext::createNewContextID() );

        osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
        osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(ds);
        traits->width = v->width();
        traits->height = v->height();
        traits->depth = 32;
        traits->stencil = 8;
        traits->alpha = 8;
        traits->pbuffer = false;
        traits->sharedContext = 0;
        traits->doubleBuffer = true;
		//traits->samples = 0;
		traits->samples = 4;
		if (AmkConfig::instance()->amk_samples >= 0)
		{
			traits->samples = AmkConfig::instance()->amk_samples;
		}

        
        this->_traits = traits;
        this->setWindowRectangle(0,0,v->width(), v->height());
        getEventQueue()->syncWindowRectangleWithGraphicsContext();
    }
    virtual ~OsgGraphicsContextAdapter(){}
    void setTraits(osg::GraphicsContext::Traits* t);

    // GraphicsContext interface
public:
    virtual bool valid() const{
        return this->_view && _view->openglContext();
    }

    virtual bool makeCurrentImplementation(){
        if(this->_view && _view->openglContext()){
            bool r = _view->openglContext()->makeCurrent(_view);
            return r;
        }
        return false;
    }
    virtual bool releaseContextImplementation(){
        return true;
    }

    void swapBuffersImplementation(){

    }

    bool setWindowRectangleImplementation(int, int, int, int){
        return true;
    }

    OsgGraphicView* _view;
};

OsgGraphicView::OsgGraphicView()
{
    setSurfaceType(QWindow::OpenGLSurface);
    setClearBeforeRendering(false);

    connect(this, SIGNAL(afterRendering()), this, SLOT(afterRendering()), Qt::DirectConnection);
    connect(this, SIGNAL(beforeRendering()), this, SLOT(beforeRendering()), Qt::DirectConnection);
    QSurfaceFormat format;
    format = QSurfaceFormat::defaultFormat();
    format.setDepthBufferSize(24);
//    format.setSamples(0);
	format.setSamples(4);
	if (AmkConfig::instance()->amk_samples >= 0)
	{
		format.setSamples(AmkConfig::instance()->amk_samples);
	}
    format.setSwapInterval(1);
    format.setAlphaBufferSize(8);
    format.setStencilBufferSize(8);
    format.setStereo( false );
    setFormat(format);
    //this->create();
    this->setPersistentOpenGLContext(true);

    _context = 0;


	_fov = 60;
	_near = 1;
	_far = 10000000;
	_viewport_width = -1;
	_viewport_height = -1;

    //_stereo = false;
}

void OsgGraphicView::setScene(osg::Node *n)
{
    this->_node = n;
    if(_viewer){
        this->_viewer->setSceneData(n);
        _viewer->home();
    }

}

void OsgGraphicView::init()
{
    _context = new OsgGraphicsContextAdapter(this);

    osgViewer::Viewer* viewer = new osgViewer::Viewer();
    osg::ref_ptr<osg::Camera> camera = viewer->getCamera();
    camera->setGraphicsContext(_context);
    camera->setViewport(new osg::Viewport(0,0,this->width(),this->height()));
    float asp = (float)this->width()/this->height();
    //EarthWrapper::instance()->__asp = asp;
	camera->setProjectionMatrixAsPerspective(60, asp, 10, 10000000);
    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    camera->setClearColor( osg::Vec4(0, 0, 0, 1) );
    camera->setClearDepth(1.0);
    //camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
    //osgDB::DatabasePager* pager = viewer->getDatabasePager();
    //pager->setDoPreCompile(false);
    //pager->setUpThreads(6,4);
    viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
    viewer->realize();
    this->_viewer = viewer;

//    EarthWrapperOptions options;
//    options.viewer = viewer;
    //    EarthWrapper::intance()->init(options);
    //viewer->addEventHandler(new osgViewer::StatsHandler);
}

void OsgGraphicView::render()
{
	_viewer->frame();
	return;

	//glPopAttrib();
	////_camera->setDrawBuffer(GL_BACK);
	////_viewer->frame();
	//_viewer->advance();
	//_viewer->eventTraversal();
	//_viewer->updateTraversal();
	//_viewer->renderingTraversals();


	//glPushAttrib(GL_ALL_ATTRIB_BITS);
	////glClientActiveTexture(GL_TEXTURE0);
	////glActiveTexture(GL_TEXTURE0);
	//QOpenGLContext::currentContext()->functions()->glActiveTexture(GL_TEXTURE0);
	//glEnable(GL_TEXTURE_2D);
	//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

void OsgGraphicView::SetGraphicsViewport(int w, int h)
{
	_viewport_width = w;
	_viewport_height = h;
	if (_viewer && _context)
	{
		_context->resized(0, 0, w, h);
		osg::Camera* camera = _viewer->getCamera();
		camera->setViewport(0, 0, w, h);
		float asp = (float)_viewport_width / _viewport_height;
		////EarthWrapper::instance()->__asp = asp;
		camera->setProjectionMatrixAsPerspective(_fov, asp, _near, _far);
		//if (_near  < 0 && _far < 0)
		//{
		//	camera->setProjectionMatrixAsPerspective(_fov, asp, 1, 10000000);
		//	//camera->setComputeNearFarMode(osg::CullSettings::COMPUTE_NEAR_USING_PRIMITIVES);
		//	//camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
		//}
		//else
		//{
		//	camera->setProjectionMatrixAsPerspective(_fov, asp, 1, 10000000);
		//	camera->setComputeNearFarMode(osg::CullSettings::COMPUTE_NEAR_USING_PRIMITIVES);
		//}
	}
}

void OsgGraphicView::SetFovNearFar(float fov, float fnear, float ffar)
{
	if (fov > 0) _fov = fov;
	if (fnear > 0) _near = fnear;
	if (ffar > 0) _far = ffar;

	if (_viewer)
	{
		osg::Camera* camera = _viewer->getCamera();
		float asp = (float)_viewport_width / _viewport_height;
		camera->setProjectionMatrixAsPerspective(_fov, asp, _near, _far);

		//if (_near <= 0 && _far <= 0)
		//{
		//	camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
		//}
		//else
		//{
		//	camera->setComputeNearFarMode(osg::CullSettings::COMPUTE_NEAR_USING_PRIMITIVES);
		//}
	}


}

//void OsgGraphicView::slotStereo(){
//    this->_stereo = !_stereo;
//}

//void OsgGraphicView::slotGotoHenan()
//{

//        //this->_eventAdapter->setManipulatorMode(EventAdapter::MM_OsgEarth);
//        osgEarth::Viewpoint vp( "Henan",113,33.6,0, 0, -70, 6e5 );
//        EarthWrapper::intance()->getManipulator()->setViewpoint(vp,3.0);

//}

//void OsgGraphicView::slotGotoHome()
//{
//    osgEarth::Viewpoint vp( "Home",113,33.6,0, 0, -90, 10e6 );
//    EarthWrapper::intance()->getManipulator()->setViewpoint(vp,3.0);
//}

void OsgGraphicView::beforeRendering()
{


}

void OsgGraphicView::afterRendering()
{
	static bool glew_init = false;
	if (glew_init == false) {
		glewInit();
		glew_init = true;
	}

	this->openglContext()->makeCurrent(this);
	QOpenGLContext::currentContext()->functions()->glUseProgram(0);

	if (_viewer == 0) {
		this->init();
	}

	this->render();

	QOpenGLContext::currentContext()->functions()->glUseProgram(0);
	_context->swapBuffers();
	this->update();
}

void OsgGraphicView::resizeEvent(QResizeEvent *e)
{
    QQuickView::resizeEvent(e);

    if(_context && _viewer){
        int w = this->width();
        int h = this->height();

		if (_viewport_width > 0 && _viewport_height > 0)
		{
			w = _viewport_width;
			h = _viewport_height;
		}
		this->SetGraphicsViewport(w, h);

        //_context->resized(0, 0, w, h);
        //osg::Camera* camera = _viewer->getCamera();
        //camera->setViewport(0,0,w,h);
        //float asp = (double)w/h;
        //EarthWrapper::instance()->__asp = asp;
        //camera->setProjectionMatrixAsPerspective(60,asp,10,10000000);
    }
}


void OsgGraphicView::mousePressEvent(QMouseEvent *event)
{
    QQuickView::mousePressEvent(event);
    if(event->isAccepted()) return;
    if(!_context){ return; }
    int button = 0;
    switch ( event->button() )
    {
        case Qt::LeftButton: button = 1; break;
        case Qt::MidButton: button = 2; break;
        case Qt::RightButton: button = 2; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
    }
    _context->getEventQueue()->mouseButtonPress( event->x(), event->y(), button );
}

void OsgGraphicView::mouseReleaseEvent(QMouseEvent *event)
{
    QQuickView::mouseReleaseEvent(event);
    if(event->isAccepted()) return;
    if(!_context){ return; }
    int button = 0;
    switch ( event->button() )
    {
        case Qt::LeftButton: button = 1; break;
        case Qt::MidButton: button = 2; break;
        case Qt::RightButton: button = 2; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
    }
    _context->getEventQueue()->mouseButtonRelease( event->x(), event->y(), button );
}

void OsgGraphicView::mouseMoveEvent(QMouseEvent *event)
{
    QQuickView::mouseMoveEvent(event);
    if(event->isAccepted()) return;
    if(!_context){ return; }
    _context->getEventQueue()->mouseMotion(event->x(), event->y());
}

void OsgGraphicView::wheelEvent(QWheelEvent *event)
{
    QQuickView::wheelEvent(event);
    if(event->isAccepted()) return;
    if(!_context){ return; }
    _context->getEventQueue()->mouseScroll(
        event->orientation() == Qt::Vertical ?
            (event->delta()<0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) :
                    (event->delta()<0 ? osgGA::GUIEventAdapter::SCROLL_LEFT : osgGA::GUIEventAdapter::SCROLL_RIGHT) );
}

void OsgGraphicView::mouseDoubleClickEvent(QMouseEvent *event)
{
    QQuickView::mouseDoubleClickEvent(event);
    if(event->isAccepted()) return;
    if(!_context){ return; }
    int button = 0;
    switch ( event->button() )
    {
        case Qt::LeftButton: button = 1; break;
        case Qt::MidButton: button = 2; break;
        case Qt::RightButton: button = 2; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
    }
    _context->getEventQueue()->mouseDoubleButtonPress(event->x(), event->y(), button);
}
